package com.mzj.saas.jobs.bill.service;

import com.mzj.saas.jobs.bill.entity.*;
import com.mzj.saas.jobs.bill.repository.*;
import com.mzj.saas.jobs.commons.DateUtils;
import com.mzj.saas.jobs.commons.Page;
import com.mzj.saas.jobs.commons.SqlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 物业账单
 */
@Service
public class PropertyBillService {
    private static Logger log = LoggerFactory.getLogger(PropertyBillService.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private HouseRepository houseRepository;
    @Autowired
    private CommunityHouseUserRepository communityHouseUserRepository;
    @Autowired
    private EstateRentBillRelationRepository estateRentBillRelationRepository;
    @Autowired
    private EstateRentBillRepository estateRentBillRepository;
    @Autowired
    private EstateExpenseBillRepository estateExpenseBillRepository;
    @Autowired
    private HouseExpenseWaterRepository houseExpenseWaterRepository;
    @Autowired
    private HouseExpenseElectricRepository houseExpenseElectricRepository;
    @Autowired
    private HouseExpenseGasRepository houseExpenseGasRepository;
    @Autowired
    private EstateExpenseBillDetailRepository estateExpenseBillDetailRepository;
    @Autowired
    private HouseExpenseBillRuleRepository houseExpenseBillRuleRepository;

    public String work() throws Exception {

        createRentBill();
        createExpenseOther();

        return "SUCCESS";
    }

    /**
     * 生成租金/押金账单明细
     *
     * @param
     */
    public void createRentBill() {
        try {
            log.info("PropertyBillService 生成租金/押金账单明细  start");

            String expenseSql = "select r.charge_type as chargeType,r.house_id as houseId ,r.price from saas_house_expense_rent r where "
                    + "r.enable_status = 1 and r.status = 1 and r.house_nature != 1 group by r.house_id,r.charge_type";

            //查询下是否有 房租/押金账单数据
            List<Map<String, Object>> rentList = jdbcTemplate.queryForList(expenseSql);
            if (CollectionUtils.isEmpty(rentList)) {
                log.info("PropertyBillService 没有收费项目-房租/押金账单数据  end");
                return;
            }

            //物业房id集合
            List<String> hIds = new ArrayList<>();
            rentList.forEach(k -> hIds.add(k.get("houseId").toString()));

            String ruleSql = "select f.house_id as houseId, r.charge_type as chargeType,r.start_time AS startTime,r.end_time AS endTime,r.close_time AS closeTime"
                    + " from saas_house_expense_rule_ref f left join saas_house_expense_bill_rule r "
                    + "on f.rule_id = r.id where r.charge_type in(8,9) and create_time is null and f.house_nature != 1 and f.house_id in " + SqlUtils.foreachIn(hIds.size());

            //查询账单生成规则数据
            List<Map<String, Object>> ruleList = jdbcTemplate.queryForList(ruleSql, hIds.toArray());
            if (CollectionUtils.isEmpty(ruleList)) {
                log.info("PropertyBillService 没有收费项目-房租/押金账单规则数据  end");
                return;
            }

            //物业房信息
            List<House> houseList = houseRepository.findByIdIn(hIds);

            //物业房信息Map
            Map<String, List<House>> houseMap = houseList.stream().collect(Collectors.groupingBy(House::getId));

            //账单生成规则信息
            Map<String, List<Map<String, Object>>> ruleMap = ruleList.stream().collect(Collectors.groupingBy((Map<String, Object> m) -> m.get("houseId") + "_" + m.get("chargeType")));
            ArrayList<Object> houseIds = new ArrayList<Object>(ruleMap.keySet());

            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

            Page page = new Page(houseIds.size(), 100);
            log.info("PropertyBillService houseIds size= {}", houseIds.size());

            final Date createTime = new Date();

            for (int i = 1; i <= page.getTotalPages(); i++) {

                fixedThreadPool.execute(new Thread() {
                    private int w;

                    public Thread set(int s) {
                        w = s;
                        return this;
                    }

                    public void run() {
                        try {
                            log.info(page.getStart(w) + "/" + houseIds.size() + "," + page.getPageSize());
                            //租金/押金账单明细
                            List<EstateRentBill> estateRentBillList = new ArrayList<EstateRentBill>();
                            for (int m = page.getStart(w); m < page.getStart(w) + page.getPageSize()
                                    && m < houseIds.size(); m++) {

                                String houseIdStr = (String) houseIds.get(m);

                                String hct[] = houseIdStr.split("_");
                                String houseId = hct[0];
                                Integer chargeType = Integer.parseInt(hct[1]);

                                //规则信息
                                List<Map<String, Object>> rList = ruleMap.get(houseIdStr);

                                if (CollectionUtils.isEmpty(rList)) {
                                    log.warn("未找到规则信息  houseId={}", houseIdStr);
                                    continue;
                                }

                                //物业房信息
                                List<House> hList = houseMap.get(houseId);

                                if (CollectionUtils.isEmpty(hList)) {
                                    log.warn("未找到物业房产信息 houseId={}", houseId);
                                    continue;
                                }
                                House house = hList.get(0);

                                //费用 单位分
                                Integer cose = 0;
                                //收费项目:8租金 9押金
                                Integer billChargeType = 2;
                                if (chargeType == 8) {
                                    billChargeType = 1;

                                }

                                for (Map<String, Object> map : rentList) {
                                    String renthouseId = (String) map.get("houseId");
                                    Integer rentchargeType = (Integer) map.get("chargeType");
                                    if (houseId.equals(renthouseId) && billChargeType.equals(rentchargeType)) {
                                        cose = (Integer) map.get("price");
                                    }
                                }

                                List<CommunityHouseUser> communityHouseUserList = communityHouseUserRepository.findByCommunityHouseIdAndStatusAndType(houseId, 1, 1);
                                if (communityHouseUserList.size() == 0) {
                                    log.warn("未找到租客信息 houseId={}", houseId);
                                    continue;
                                }
                                String matchUserId = communityHouseUserList.get(0).getUserId();

                                for (Map<String, Object> rMap : rList) {
                                    EstateRentBill estateRentBill = new EstateRentBill();
                                    estateRentBill.setHouseId(houseId);
                                    estateRentBill.setChargeType(billChargeType);
                                    estateRentBill.setCommunityId(house.getResidenceCommunityId());
                                    estateRentBill.setBuildingNum(house.getBuildingNum());
                                    estateRentBill.setUnit(house.getUnit());
                                    estateRentBill.setOrgId(house.getHousePropertyId());
                                    estateRentBill.setPaymentStatus(0);
                                    estateRentBill.setRoomNumber(house.getRoomNumber());
                                    estateRentBill.setMatchUserId(matchUserId);
                                    estateRentBill.setCost(cose);
                                    estateRentBill.setMeasureUnit("元/(月/季度/半年/年)");
                                    estateRentBill.setStartTime(DateUtils.convert(rMap.get("startTime").toString(), DateUtils.FORMAT_DATETIME_14));
                                    estateRentBill.setEndTime(DateUtils.convert(rMap.get("endTime").toString(), DateUtils.FORMAT_DATETIME_14));
                                    estateRentBill.setShouldPayTime(DateUtils.convert(rMap.get("closeTime").toString(), DateUtils.FORMAT_DATETIME_14));
                                    estateRentBill.setCreateTime(createTime);
                                    estateRentBill.setBillType(1);
                                    estateRentBillList.add(estateRentBill);
                                }
                            }

                            if (!CollectionUtils.isEmpty(estateRentBillList)) {
                                estateRentBillRepository.saveAll(estateRentBillList);

                                //物业房合同与租金/押金账单关系表
                                List<EstateRentBillRelation> estateRelationList = new ArrayList<EstateRentBillRelation>();

                                estateRentBillList.forEach(r -> {
                                    EstateRentBillRelation estateRelation = new EstateRentBillRelation();
                                    estateRelation.setHouseId(r.getHouseId());
                                    estateRelation.setRentBillId(r.getId());
                                    estateRelation.setMatchUserId(r.getMatchUserId());
                                    estateRelationList.add(estateRelation);

                                    //
                                    String sql = "SELECT r.id\n" +
                                            "FROM saas_house_expense_rule_ref f LEFT JOIN saas_house_expense_bill_rule r\n" +
                                            "ON f.rule_id = r.id WHERE r.charge_type = ? AND create_time is null AND f.house_id = ? ";

                                    List<Object> args = new ArrayList<>();
                                    //收费项目:8租金 9押金
                                    Integer ruleChargeType = 9;
                                    if (r.getChargeType().equals(1)) {
                                        ruleChargeType = 8;
                                    }
                                    args.add(ruleChargeType);
                                    args.add(r.getHouseId());
                                    List<String> ruleList = jdbcTemplate.queryForList(sql, args.toArray(), String.class);

                                    List<HouseExpenseBillRule> rList = houseExpenseBillRuleRepository.findAllById(ruleList);
                                    rList.forEach(r2 -> r2.setCreateTime(createTime));
                                    houseExpenseBillRuleRepository.saveAll(rList);

                                });
                                estateRentBillRelationRepository.saveAll(estateRelationList);

                            }

                            log.info(page.getStart(w) + ",save = " + estateRentBillList.size());

                        } catch (Exception e) {
                            log.error("生成租金/押金账单明细异常  w =" + page.getStart(w) + "," + e.getMessage(), e);
                        }
                    }

                }.set(i));

            }

            fixedThreadPool.shutdown();
            // 等待子线程结束，再继续执行下面的代码
            fixedThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

            log.info("PropertyBillService 生成租金/押金账单明细  end");

        } catch (Exception e) {
            log.error("生成租金/押金账单明细异常  " + e.getMessage(), e);
        }

    }


    /**
     * 其它费用账单明细
     */
    public void createExpenseOther() {

        try {
            log.info("PropertyBillService 生成其它费用账单明细  start");

            String expenseSql = "select r.charge_type as chargeType,h.house_id as houseId ,r.price,r.unit from saas_house_expense_other r inner join saas_house_expense_house h " +
                    "on r.id=h.expense_id  where "
                    + "r.enable_status = 1 and r.status = 1 and house_nature != 1 group by h.house_id,r.charge_type";

            //查询下是否有 其它费用账单数据
            List<Map<String, Object>> rentList = jdbcTemplate.queryForList(expenseSql);
            if (CollectionUtils.isEmpty(rentList)) {
                log.info("PropertyBillService 没有收费项目-其它费用账单数据  end");
                return;
            }

            //物业房id集合
            List<String> hIds = new ArrayList<>();
            rentList.forEach(k -> hIds.add(k.get("houseId").toString()));

            String ruleSql = "select f.house_id as houseId, r.charge_type as chargeType,r.start_time AS startTime,r.end_time AS endTime,r.close_time AS closeTime,r.id AS rId"
                    + " from saas_house_expense_rule_ref f left join saas_house_expense_bill_rule r "
                    + "on f.rule_id = r.id where r.charge_type in(1, 2, 3, 4, 5, 6, 7) and create_time is null and house_nature != 1 and f.house_id in " + SqlUtils.foreachIn(hIds.size());

            //查询账单生成规则数据
            List<Map<String, Object>> ruleList = jdbcTemplate.queryForList(ruleSql, hIds.toArray());
            if (CollectionUtils.isEmpty(ruleList)) {
                log.info("PropertyBillService 没有收费项目-其它费用账单规则数据  end");
                return;
            }

            //物业房信息
            List<House> houseList = houseRepository.findByIdIn(hIds);

            //物业房信息Map
            Map<String, List<House>> houseMap = houseList.stream().collect(Collectors.groupingBy(House::getId));

            //账单生成规则信息
            Map<String, List<Map<String, Object>>> ruleMap = ruleList.stream().collect(Collectors.groupingBy((Map<String, Object> m) -> m.get("houseId") + "_" + m.get("chargeType")));
            ArrayList<Object> houseIds = new ArrayList<Object>(ruleMap.keySet());

            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

            Page page = new Page(houseIds.size(), 100);
            log.info("PropertyBillService houseIds size= {}", houseIds.size());

            final Date createTime = new Date();

            for (int i = 1; i <= page.getTotalPages(); i++) {

                fixedThreadPool.execute(new Thread() {
                    private int w;

                    public Thread set(int s) {
                        w = s;
                        return this;
                    }

                    public void run() {
                        try {
                            log.info(page.getStart(w) + "/" + houseIds.size() + "," + page.getPageSize());
                            //其它费用账单明细 saas_estate_expense_bill
                            List<EstateExpenseBill> estateExpenseBillList = new ArrayList<EstateExpenseBill>();
                            for (int m = page.getStart(w); m < page.getStart(w) + page.getPageSize()
                                    && m < houseIds.size(); m++) {

                                String houseIdStr = (String) houseIds.get(m);

                                String hct[] = houseIdStr.split("_");
                                String houseId = hct[0];
                                Integer chargeType = Integer.parseInt(hct[1]);

                                //规则信息
                                List<Map<String, Object>> rList = ruleMap.get(houseIdStr);

                                if (CollectionUtils.isEmpty(rList)) {
                                    log.warn("未找到规则信息  houseId={}", houseIdStr);
                                    continue;
                                }

                                //物业房信息
                                List<House> hList = houseMap.get(houseId);

                                if (CollectionUtils.isEmpty(hList)) {
                                    log.warn("未找到物业房产信息 houseId={}", houseId);
                                    continue;
                                }
                                House house = hList.get(0);

                                //费用 单位分
                                Integer cose = 0;
                                //计量单位，如：月/度/平方
                                String unit = null;

                                for (Map<String, Object> map : rentList) {
                                    String renthouseId = (String) map.get("houseId");
                                    Integer rentchargeType = (Integer) map.get("chargeType");
                                    if (houseId.equals(renthouseId) && chargeType.equals(rentchargeType)) {
                                        cose = (Integer) map.get("price");
                                        String unitTemp = (String) map.get("unit");

                                        unit = unitTemp;
                                    }
                                }

                                List<CommunityHouseUser> communityHouseUserList = communityHouseUserRepository.findByCommunityHouseIdAndStatusAndType(houseId, 1, 1);
                                if (communityHouseUserList.size() == 0) {
                                    log.warn("未找到租客信息 houseId={}", houseId);
                                    continue;
                                }
                                String matchUserId = communityHouseUserList.get(0).getUserId();

                                for (Map<String, Object> rMap : rList) {
                                    EstateExpenseBill estateExpenseBill = new EstateExpenseBill();

                                    Integer tempUnitPrice = cose;
                                    //
                                    switch ((Integer) rMap.get("chargeType")) {

                                        case 1:
                                            HouseExpenseWater houseExpenseWater = houseExpenseWaterRepository.findByRuleId((String) rMap.get("rId"));
                                            Integer waterUsage = houseExpenseWater.getWaterUsage();
                                            cose = cose * waterUsage;
                                            break;
                                        case 2:
                                            HouseExpenseElectric houseExpenseElectric = houseExpenseElectricRepository.findByRuleId((String) rMap.get("rId"));
                                            Integer electricUsage = houseExpenseElectric.getElectricUsage();
                                            cose = cose * electricUsage;
                                            break;
                                        case 6:
                                            HouseExpenseGas houseExpenseGas = houseExpenseGasRepository.findByRuleId((String) rMap.get("rId"));
                                            Integer gasUsage = houseExpenseGas.getGasUsage();
                                            cose = cose * gasUsage;
                                            break;
                                        case 7:
                                            //按房屋面积 * 收费项目的单价 = 待缴费金额
                                            Integer area = Integer.valueOf(house.getArea().setScale(0, BigDecimal.ROUND_HALF_UP).toString());
                                            cose = cose * area;
                                            break;
                                    }

                                    estateExpenseBill.setHouseId(houseId);
                                    estateExpenseBill.setChargeType(chargeType);
                                    estateExpenseBill.setCommunityId(house.getResidenceCommunityId());
                                    estateExpenseBill.setBuildingNum(house.getBuildingNum());
                                    estateExpenseBill.setUnit(house.getUnit());
                                    estateExpenseBill.setOrgId(house.getHousePropertyId());
                                    estateExpenseBill.setPaymentStatus(0);
                                    estateExpenseBill.setRoomNumber(house.getRoomNumber());
                                    estateExpenseBill.setMatchUserId(matchUserId);
                                    estateExpenseBill.setCost(cose);
                                    estateExpenseBill.setMeasureUnit(unit);
                                    estateExpenseBill.setStartTime(DateUtils.convert(rMap.get("startTime").toString(), DateUtils.FORMAT_DATETIME_14));
                                    estateExpenseBill.setEndTime(DateUtils.convert(rMap.get("endTime").toString(), DateUtils.FORMAT_DATETIME_14));
                                    //estateExpenseBill.setPaymentTime(DateUtils.convert(rMap.get("closeTime").toString(), DateUtils.FORMAT_DATETIME_14));
                                    estateExpenseBill.setCreateTime(createTime);
                                    estateExpenseBill.setAuditStatus(0);      //'审核状态：0未审核 1已审核'
                                    estateExpenseBill.setBillType(1);
                                    estateExpenseBillList.add(estateExpenseBill);
                                    EstateExpenseBill save = estateExpenseBillRepository.save(estateExpenseBill);

                                    EstateExpenseBillDetail estateExpenseBillDetail = new EstateExpenseBillDetail();
                                    estateExpenseBillDetail.setExpenseBillId(save.getId());
                                    estateExpenseBillDetail.setChargeType(save.getChargeType());
                                    switch (save.getChargeType()) {   // 1水费 2电费 3物业费 4宽带费 5电视费 6煤气费 7暖气费

                                        case 1:
                                            HouseExpenseWater houseExpenseWater = houseExpenseWaterRepository.findByRuleId((String) rMap.get("rId"));
                                            estateExpenseBillDetail.setWaterStartNum(houseExpenseWater.getWaterStartNum());
                                            estateExpenseBillDetail.setWaterEndNum(houseExpenseWater.getWaterEndNum());
                                            estateExpenseBillDetail.setWaterUsage(Double.valueOf(houseExpenseWater.getWaterUsage()));
                                            estateExpenseBillDetail.setWaterUnitPrice(tempUnitPrice);

                                            break;
                                        case 2:
                                            HouseExpenseElectric houseExpenseElectric = houseExpenseElectricRepository.findByRuleId((String) rMap.get("rId"));
                                            estateExpenseBillDetail.setElectricStartNum(houseExpenseElectric.getElectricStartNum());
                                            estateExpenseBillDetail.setElectricEndNum(houseExpenseElectric.getElectricEndNum());
                                            estateExpenseBillDetail.setElectricUsage(Double.valueOf(houseExpenseElectric.getElectricUsage()));
                                            estateExpenseBillDetail.setElectricUnitPrice(tempUnitPrice);
                                            break;
                                        case 3:
                                            estateExpenseBillDetail.setPropertyFee(save.getCost());
                                            break;
                                        case 4:
                                            estateExpenseBillDetail.setBroadBandCharges(save.getCost());
                                            break;
                                        case 5:
                                            estateExpenseBillDetail.setTvFee(save.getCost());
                                            break;
                                        case 6:
                                            HouseExpenseGas houseExpenseGas = houseExpenseGasRepository.findByRuleId((String) rMap.get("rId"));
                                            estateExpenseBillDetail.setGasStartNum(houseExpenseGas.getGasStartNum());
                                            estateExpenseBillDetail.setGasEndNum(houseExpenseGas.getGasEndNum());
                                            estateExpenseBillDetail.setGasUsage(Double.valueOf(houseExpenseGas.getGasUsage()));
                                            estateExpenseBillDetail.setGasUnitPrice(tempUnitPrice);
                                            break;
                                        case 7:
                                            estateExpenseBillDetail.setHeatingBill(tempUnitPrice);
                                            break;
                                    }

                                    estateExpenseBillDetailRepository.save(estateExpenseBillDetail);

                                }
                            }

                            if (!CollectionUtils.isEmpty(estateExpenseBillList)) {


                                List<String> ids = new ArrayList<>();
                                estateExpenseBillList.forEach(r -> {
                                    ids.add(r.getHouseId());

                                    String sql = "SELECT r.id\n" +
                                            "FROM saas_house_expense_rule_ref f LEFT JOIN saas_house_expense_bill_rule r\n" +
                                            "ON f.rule_id = r.id WHERE  r.charge_type = ? AND create_time is null AND f.house_id = ?  ";

                                    List<Object> args = new ArrayList<>();

                                    args.add(r.getChargeType());
                                    args.add(r.getHouseId());
                                    List<String> ruleList = jdbcTemplate.queryForList(sql, args.toArray(), String.class);

                                    List<HouseExpenseBillRule> rList = houseExpenseBillRuleRepository.findAllById(ruleList);
                                    rList.forEach(r2 -> r2.setCreateTime(createTime));
                                    houseExpenseBillRuleRepository.saveAll(rList);

                                });

                            }

                            log.info(page.getStart(w) + ",save = " + estateExpenseBillList.size());

                        } catch (Exception e) {
                            log.error("生成其它费用账单明细异常  w =" + page.getStart(w) + "," + e.getMessage(), e);
                            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                        }
                    }

                }.set(i));

            }


            fixedThreadPool.shutdown();
            // 等待子线程结束，再继续执行下面的代码
            fixedThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

            log.info("PropertyBillService 生成其它费用账单明细   end");
        } catch (Exception e) {
            log.error("生成其它费用账单明细异常  " + e.getMessage(), e);
        }
    }


}
